#include "controllers/ControladorRecurso.h"
#include <cstddef>
#include <string>
#include <sstream>
#include <stdexcept>
#include "models/Recurso.h"
#include "models/Comentario.h"


using namespace std;


ControladorRecurso::ControladorRecurso() :recursosSistema(){

	this->carpetaAColaborar = NULL;
	this->rec = NULL;
	this->archivoAComentar = NULL;
	this->cantidadComentarios = 0;
	this->comentarioAAgregar = NULL;
	this->raiz = new Carpeta();
	this->raiz->setNombre("raiz");
	this->raiz->setRuta("/");
	this->raiz->setTipoRecurso("carpeta");
	this->recursosSistema.insert(raiz);
}

Recurso* ControladorRecurso::getRecurso(){
	return this->rec;
}

Carpeta* ControladorRecurso::getCarpetaAColaborar(){
	return this->carpetaAColaborar;
}

// Precondicion: La carpeta de nombre 'nombreCarpeta' existe bajo la ruta 'ruta'
Carpeta* ControladorRecurso::setCarpetaAColaborar(string nombreCarpeta, string ruta){
   return this->raiz;
}

//set<Usuario> ControladorRecurso::setCreador(Usuario user)
//{};

void ControladorRecurso::crearRecurso(string nombre, string descripcion, string tipo)
{
	validarCantidadCaracteres(nombre);
	validarContieneBarra(nombre);

	if(tipo.compare("archivo") == 0)
	{
		this->rec = new Archivo();
		this->rec->setNombre(nombre);
		this->rec->setDescripcion(descripcion);
		this->rec->setTipoRecurso(tipo);
	}
	else if(tipo.compare("carpeta") == 0)
	{
		this->rec = new Carpeta();
		this->rec->setNombre(nombre);
		this->rec->setDescripcion(descripcion);
		this->rec->setTipoRecurso(tipo);
	}

};

void ControladorRecurso::indicarCreador(string nickName)
{
	this->rec->setCreador(nickName);
};

void ControladorRecurso::indicarDestino(string rutaDestino)
{
	int i=0;
	i = rutaDestino.size();
	if(i>0 && rutaDestino[i-1] != '/'){
		rutaDestino.append("/");
		
	}
	verificarRuta(rutaDestino);
	agregarRecursoHijo(rutaDestino);
	this->rec->setRuta(rutaDestino);

	
	// agrego el recurso a recurso propios del user
	Usuario * user = this->CU->obtenerUsuarioDelSistema(this->rec->getCreador());
	set<Recurso*> recs = user->getRecursosPropios();
	recs.insert(this->rec);

	//agrego accion al historial
	agregarAccion(creacion,user,this->rec);

	this->recursosSistema.insert(this->rec);
};

bool ControladorRecurso::registrarUsuarioColaborador(){
	return false;
};

//set<Carpeta> ControladorRecurso::elegirUsuario(string nickName)
//{};

//set<Usuario> ControladorRecurso::elegirCarpetaAColaborar(string nombreCarpeta)
//{};

bool ControladorRecurso::elegirArchivoAModificar(string nombreArchivo, string rutaArchivo){

	bool encontrado = false;
	bool seguir = true;
	set<Recurso*>::iterator it = this->recursosSistema.begin();
	while (it != this->recursosSistema.end()  && (encontrado == false) && (seguir)) {

		Recurso* r = (*it);

		if (( r->getTipoRecurso().compare("archivo") == 0 ) &&
			(r->getNombre().compare(nombreArchivo) == 0 ) &&
			(r->getRuta().compare(rutaArchivo)) == 0)
		{
			seguir = false;
			encontrado = true;
		}
		++it;
	}

	return ( encontrado );

}


void ControladorRecurso::ingresarNuevaDescripcionArchivo(string nombreArchivo, string rutaArchivo, string descripcion){

	bool seguir = false;
	set<Recurso*>::iterator it = this->recursosSistema.begin();
	while (it != this->recursosSistema.end() && (seguir == false)) {

		if (( (*it)->getTipoRecurso().compare("archivo") == 0 ) &&
			((*it)->getNombre().compare(nombreArchivo) == 0 ) &&
			((*it)->getRuta().compare(rutaArchivo)) == 0)
		{
			this->rec = (*it);
			seguir = true;
		}
		++it;
	}

	this->rec->setDescripcion(descripcion);

}

void ControladorRecurso::registrarNuevoComentario(string ruta, string nombreArchivo){
    bool  encontrado, seguir = false;
	set<Recurso*>::iterator it = this->recursosSistema.begin();
	while (it != this->recursosSistema.end()  && (encontrado == false) && (seguir == false)) {
		Recurso* r = (*it);
		if (( r->getTipoRecurso().compare("archivo") == 0 ) &&
			(r->getNombre().compare(nombreArchivo) == 0 ) &&
			(r->getRuta().compare(ruta)) == 0)
		{
			seguir = true;
			encontrado = true;
			this->archivoAComentar = (Archivo*)(*it);
		}
		++it;
	}
	if (!encontrado)
        throw std::out_of_range("El archivo a comentar no existe en el sistema");
    else
        this->archivoAComentar->imprimirComentariosArchivo();

};

void ControladorRecurso::ingresarNuevoComentario(){
        set<Comentario*> coms = this->archivoAComentar->getComentariosArchivo();
        this->comentarioAAgregar = new Comentario(this->archivoAComentar->cantidadComentarios()+1);
        coms.insert(this->comentarioAAgregar);
        this->archivoAComentar->modificar("modificacion");
}



void ControladorRecurso::ingresarComentarioRespuesta(int idComentario){
    set<Comentario*> coms = this->archivoAComentar->getComentariosArchivo();
    set<Comentario*>::iterator it = coms.begin();
    bool encontre = false;
    Comentario* c;
    Comentario* aResponder;
    while (it!=coms.end()&&(!encontre)){
        c = (*it);
        aResponder = c->encontrarComentarioID(idComentario, encontre);
        ++it;
    }
    if (encontre){
        aResponder->asignarRespuesta(this->comentarioAAgregar);
        this->archivoAComentar->modificar("modificacion");
    }
    else
        throw std::out_of_range("El comentario a responder no existe en el sistema");

}

void ControladorRecurso::imprimirHistorialUsuario(string nickName){
    if (this->historialAcciones.size() == 0){
        vector<string*> v;
        set<Accion*>::iterator it;
        for (it = this->historialAcciones.begin(); it != this->historialAcciones.end(); it++){
            Accion * ac = (*it);
            string* linea = NULL;
            string nombre = ac->ObtenerUsuarioAccion();
            if (nombre.compare(nickName)==0){
                TipoAccion t = ac->getTipo();
                switch (t){
                case 0:
                    linea->append("creacion");
                    break;
                case 1:
                    linea->append("modificacion");
                    break;
                case 2:
                    linea->append("destruccion");
                    break;
                };
                linea->append(" ");
                int y = int(ac->getFecha().getYear());
                int m = int(ac->getFecha().getMonth());
                int d = int(ac->getFecha().getDay());
                string anio;
                string mes;
                string dia;
                ostringstream convertY;
                ostringstream convertM;
                ostringstream convertD;
                convertY << y;
                anio = convertY.str();
                convertM << m;
                mes = convertM.str();
                convertD << d;
                dia = convertD.str();
                linea->append (" ");
                linea->append(anio);
                linea->append("/");
                linea->append(mes);
                linea->append("/");
                linea->append(dia);
                linea->append(" ");
                linea->append(ac->getArchivo()->getRuta());
                linea->append(ac->getArchivo()->getNombre());
                v.push_back(linea);
                unsigned sz = v.size();
                for (unsigned i=0; i<sz; i++){
                    cout << v[i] << "\n";
                    }
            }
        }
	}
}

bool ControladorRecurso::elegirCarpetaAColaborar(string nombreUsuario, string nombreCarpeta, string rutaCarpeta){

	bool encontrado = false;
	bool seguir = false;

	Carpeta* c;
	set<Recurso*>::iterator it = this->recursosSistema.begin();
	while (it != this->recursosSistema.end()  && (encontrado == false) && (seguir)) {

		c = (Carpeta*)(*it);

		if (( c->getTipoRecurso().compare("carpeta") == 0 ) &&
			(c->getNombre().compare(nombreCarpeta) == 0 ) &&
			(c->getRuta().compare(rutaCarpeta)) == 0)
		{
			seguir = false;
			encontrado = true;
		}
		++it;
	}

	if (encontrado){
		this->carpetaAColaborar = c;
		return encontrado;
	}else{
		return encontrado;
	}

}


void ControladorRecurso::imprimirArchivosSistema(){
	set<Recurso*>::iterator it;
	for (it = this->recursosSistema.begin(); it != this->recursosSistema.end(); ++it) {
			if ( (*it)->getTipoRecurso().compare("archivo") == 0 ){
				Archivo * a = (Archivo*)(*it);
				a->imprimirInfoArchivo();
			}
	}
}

void ControladorRecurso::verificarRuta(string ruta){
	string tipo = this->rec->getTipoRecurso();
	

	if(!ruta.empty()){

	existeRuta(ruta);

		if(ruta.compare("raiz/") !=0)
		{
		validarPermisoCarpeta(ruta);
		}

      set<Recurso*> recs = obtenerRecursosPorRuta(ruta,tipo);

	  if(!recs.empty() && recs.size()>0)
	{	
		set<Recurso*>::iterator it;
		for (it = recs.begin(); it != recs.end(); it++)
	    {
			Recurso* dato =(*it);

			if(dato->getTipoRecurso().compare(tipo)==0)
			{
	           enUsoRecurso(dato->getNombre());

			}
		}

	}
	
	}
	else
	{
		throw std::out_of_range("La ruta no puede ser vacia");
	}
};

void ControladorRecurso::validarCantidadCaracteres(string palabra){
	if(palabra.length()<1 || palabra.length()>8)
		throw std::out_of_range("El nombre puede tener entre 1 y 8 caracteres.");

}

void ControladorRecurso::validarContieneBarra(string s){
bool resultado = false;
	if(!s.empty()){
		for(int i=0;i<s.length();i++){
		if(s[i]== '/'){
			resultado = true;
		}
		}
	}
	if(resultado)
		throw std::out_of_range("El nombre del archivo no puede tener el caracter '/'.");

}

set<Recurso*> ControladorRecurso::obtenerRecursosPorRuta(string ruta,string tipo){
	set<Recurso*>::iterator it;
	set<Recurso*> lista;
	for (it = this->recursosSistema.begin(); it != this->recursosSistema.end(); it++)
	{
		Recurso * r = (*it);
		if(r->getRuta().compare(ruta)==0
			&& r->getTipoRecurso().compare(tipo)==0){
				lista.insert(r);
		}
	}
		return lista;
};

void ControladorRecurso::enUsoRecurso(string nombre){
	string archivo = this->rec->getNombre();
	int sz = nombre.size();
	int i = 0;
	bool distinto = false;
    if (archivo.size() == sz)
	{
		while(i < sz && !distinto){
			 if (tolower(nombre[i]) != tolower(archivo[i])){
				 distinto = true;
			 }
			 i++;
		}

	}
	if(!distinto)
		throw std::out_of_range("Ya existe un recurso con ese nombre.");

};

void ControladorRecurso::ingresarUsuarioYTexto(string nickName, string texto){
    this->comentarioAAgregar->setNickName(nickName);
    this->comentarioAAgregar->setContenido(texto);
};

void ControladorRecurso::split(vector<string> &tokens, const string &text, char sep) {
  int start = 0, end = 0;
  while ((end = text.find(sep, start)) != string::npos) {
    tokens.push_back(text.substr(start, end - start));
    start = end + 1;
  }
  tokens.push_back(text.substr(start));
}

void ControladorRecurso::validarPermisoCarpeta(string ruta){
	vector<string> v;
	split(v,ruta,'/');
	vector<string>::iterator it;
	vector<string>::iterator nom;
	set<Colaborador*>::iterator col;
	bool encontre = false;

	it = v.begin();
	string dato_ruta = "";
	
	while(!encontre && it!= v.end()){
		
     	string nombre = (*it);

		if(nombre.size() >0)
		{
			dato_ruta.append(nombre);		
			dato_ruta.append("/");

			if(dato_ruta.size() < ruta.size())
			{
			nom = it + 1;
			nombre = (*nom);
			Carpeta * c = obtenerCarpeta(nombre,dato_ruta);

			if(c->getCreador().compare(this->rec->getCreador())==0)
			{
				encontre = true;
			}
			else
			{
			set<Colaborador*> colaboradores = c->obtenerColaboradoresCarpeta();
			col = colaboradores.begin();
			while(!encontre && col != colaboradores.end()){
				Colaborador * colab = (*col);
				if(colab->getUsuario().compare(nombre)==0)
			{
				encontre = true;
			}
				col++;
			}
			}
			}

		}
		it++;
	}
	if(!encontre)
		throw std::out_of_range("No tiene permisos para crear el recurso en sea ruta.");
}

Carpeta * ControladorRecurso::obtenerCarpeta(string nombre,string ruta){
	set<Recurso*>::iterator it;
	Carpeta* c = NULL;

	for (it = this->recursosSistema.begin(); it != this->recursosSistema.end(); it++)
	{
		Recurso * r = (*it);
		if(r->getRuta().compare(ruta)==0
			&& r->getTipoRecurso().compare("carpeta")==0
			&& r->getNombre().compare(nombre)==0){

				c = (Carpeta*)(*it);
		}
	}
		return c;

};

void ControladorRecurso::agregarRecursoHijo(string ruta){

	Carpeta* c;

	if(ruta.compare("raiz/")==0)
	{
		c = obtenerCarpeta("raiz","/");
	}
	else
	{
	int i = ruta.find_first_of('/');
	string dato_ruta = ruta.substr(0,i + 1);
	int ultimo =ruta.size() -(dato_ruta.size() + 1);
	string nombre = ruta.substr(i + 1,ultimo);
	c = obtenerCarpeta(nombre,dato_ruta);
	}

	set<Recurso*>hijos = c->obtenerRecursosHijos();
	hijos.insert(this->rec);

};





void ControladorRecurso::verInformacionCarpeta(){

	this->raiz->imprimirNombreCarpetaEHijos();
};


void ControladorRecurso::elegirCarpetaSistema(string nombreCarpeta, string rutaCarpeta){

	bool encontrado = false;
	bool seguir = false;

	Carpeta* c;
	set<Recurso*>::iterator it = this->recursosSistema.begin();
	while (it != this->recursosSistema.end()  && (encontrado == false) && (seguir)) {

		c = (Carpeta*)(*it);

		if (( c->getTipoRecurso().compare("carpeta") == 0 ) &&
			(c->getNombre().compare(nombreCarpeta) == 0 ) &&
			(c->getRuta().compare(rutaCarpeta)) == 0)
		{
			seguir = false;
			encontrado = true;
		}
		++it;
	}

	if (encontrado){

		cout << "Nombre: " + c->getNombre() << endl;

		cout << "Descripcion: " + c->getDescripcion() << endl;

		if (c->obtenerColaboradoresCarpeta().size() != 0){
			cout << "Colaboradores: " << endl;
			set<Colaborador*>::iterator it;
			Colaborador * col;
			for (it = c->obtenerColaboradoresCarpeta().begin(); it != c->obtenerColaboradoresCarpeta().end(); it++)
			{
				col = (Colaborador*)(*it);
				cout << "Nombre: " + col->getUsuario() + ", Fecha de agregado: "
					 << col->getFechaAgregado().getYear() <<"/"<< col->getFechaAgregado().getMonth() <<"/"<< col->getFechaAgregado().getDay() << endl;
			}
		}

		cout << "Creador: " + c->getCreador() << endl;

		if (c->obtenerRecursosHijos().size() != 0){

			cout << "Recursos contenidos: " << endl;
			set<Recurso*>::iterator it;
			Recurso * r;
			for (it = c->obtenerRecursosHijos().begin(); it != c->obtenerRecursosHijos().end(); it++)
			{
				r = (Recurso*)(*it);
				cout << "Ubicacion: " + r->getRuta() + ", nombre: " + r->getNombre() + ", tipo: " + r->getTipoRecurso() << endl;
			}
		}

	}else{

	}

}

void ControladorRecurso::elegirArchivoSistema(int id){

	set<Recurso*>::iterator it;
	Archivo * a;
	for (it = this->recursosSistema.begin(); it != this->recursosSistema.end(); ++it) {

		    a = (Archivo*)(*it);
		    if ( a->getId() == id ){

			}
	}
}

void ControladorRecurso::verInformacionArchivo(){

	set<Recurso*>::iterator it;
	for (it = this->recursosSistema.begin(); it != this->recursosSistema.end(); ++it) {
			if ( (*it)->getTipoRecurso().compare("archivo") == 0 ){

				Archivo * a = (Archivo*)(*it);

				cout << "Nombre: " + a->getNombre() << endl;
				cout <<	"Descripcion: " + a->getDescripcion() << endl;
				cout << "Fecha de creacion: " << a->getFechaCreacion().getYear() <<"/"<< a->getFechaCreacion().getMonth() <<"/"<< a->getFechaCreacion().getDay() << endl;
				cout << "Fecha de ultimo acceso: " << a->getFechaUltimoAcceso().getYear() <<"/"<< a->getFechaUltimoAcceso().getMonth() <<"/"<< a->getFechaUltimoAcceso().getDay() << endl;
				cout << "Creador: " + a->getCreador();

				if (this->historialAcciones.size() != 0){

					cout << "Acciones: " << endl;
					set<Accion*>::iterator it;
					Accion * acc;
					for (it = this->historialAcciones.begin(); it != this->historialAcciones.end(); it++)
					{
						acc = (*it);

						if ((acc->getArchivo()->getNombre().compare(a->getNombre()) == 0) && acc->getArchivo()->getRuta().compare(a->getRuta())){

							cout << "Fecha de realizada: " << acc->getFecha().getYear() <<"/"<< acc->getFecha().getMonth() <<"/"<< acc->getFecha().getDay() << endl;
							cout << endl;
							cout << "Ubicacion: " + acc->getArchivo()->getRuta();

						}
					}
				}

				if (a->getComentariosArchivo().size() != 0){

					a->imprimirComentariosArchivo();
				}


			}
	}
}




void ControladorRecurso::existeRuta(string ruta){
	
	int i = 0;
	int ultimo = 0;
	i = ruta.find_first_of('/');
	Carpeta* c = NULL;
	if(ruta.size()>i && ruta.compare("raiz/")!=0)
	{
	string dato_ruta = ruta.substr(0,i + 1);
	ultimo =ruta.size() -(dato_ruta.size() + 1);
	string nombre = ruta.substr(i + 1, ultimo );

	 c = obtenerCarpeta(nombre,dato_ruta);
	 if(c==NULL)
          throw std::out_of_range("La ruta no existe.");

	}

}

void ControladorRecurso::agregarAccion(TipoAccion tipo,Usuario* user,Recurso* rec){

	DateTime d = DateTime();
	if(rec->getTipoRecurso().compare("Archivo")==0)
	{
	Archivo* arch = (Archivo*)(rec);
	Accion* acc = new Accion(user,arch,d);
	acc->setTipo(tipo);
	set<Accion*> his = this->getHistorialAcciones();
	his.insert(acc);
	}
	else{
     Accion* acc = new Accion();
	 acc->setFecha(d);
	 acc->setUsuario(user);
	 acc->setTipo(tipo);
	 set<Accion*> his = this->getHistorialAcciones();
	 his.insert(acc);
	}

};

set<Accion*> ControladorRecurso::getHistorialAcciones(){
	return this->historialAcciones;
};

void ControladorRecurso::setInterfazControladorUsuario(IControladorUsuario* CU){
	this->CU=(ControladorUsuario*)CU;
}
